#
#   api_geocode.py
#
#   David Janes
#   2008.11.22
#
#	Copyright 2008, 2009 David Janes
#
#	Interface to Geocoders
#

import string
import pprint
import os

import bm_work
import bm_uri
import bm_extract
import bm_cfg
import djolt

from bm_log import Log

import api
import api_google

import vcard
import hcard

try:
	import json
except:
	import simplejson as json

akeys = [
	vcard.PostOfficeBox,
	vcard.ExtendedAddress,
	vcard.StreetAddress,
	vcard.Locality,
	vcard.Region,
	vcard.PostalCode,
	vcard.CountryName,
]

def make_address(ruled, itemd = None):
	avalues = []

	for akey in akeys:
		value = ruled.get(akey)
		if value and itemd:
			value = djolt.Template(value).Render(itemd)

		avalues.append(value)

	return	", ".join(filter(None, avalues))

class Geocode(api.APIBase):
	"""Geocode WORK-like dictionaries"""

	_required_properties = []
	_geocoded = None

	def __init__(self, address_geocoder = None, **ad):
		self.address_geocoder = address_geocoder

		api.APIBase.__init__(self, **ad)

	def GetMeta(self):
		if self._geocoded == None:
			self.Fetch()

		return	self._geocoded

	def CustomizeReset(self):
		self._geocoded = None

	def Fetch(self):
		#
		#	Get or build the 'address'
		#
		address = self._request_meta.get("address")
		if not address:
			address = make_address(self._request_meta)

		if not address:
			raise	ValueError, "missing property 'address'"

		#
		#	Need one of these
		#
		if not self.address_geocoder:
			self.address_geocoder = GoogleAddressGeocoder(
				api_key = bm_cfg.cfg.as_string('google_maps.api_key'),
				referer = "http://code.davidjanes.com",
			)

		#
		#	Find the ll 
		#
		self._geocoded = {}

		ll = self.address_geocoder.Geocode(address)
		if ll:
			api.add_latlon(self._geocoded, ll[0], ll[1])

class GeocodeIterator(api.APIBase):
	def __init__(self, **ad):
		api.APIBase.__init__(self, **ad)

	def IterItems(self):
		for itemd in self._request_items:
			address = self._FindAddressInItem(itemd)
			if not address:
				yield	itemd
			else:
				try:
					d = {}
					d.update(itemd)
					d.update(Geocode(address = address).response)

					yield	d
				except GeneratorExit:
					raise
				except:
					Log("ignoring unwanted exception", address = address, exception = True)
					yield	itemd

	def _FindAddressInItem(self, itemd):
		#
		#	User encoded and address by Djolt rules
		#
		address = make_address(self._request_meta, itemd)
		if address:
			return	address

		#
		#	An hCard in the Address
		#
		hd = itemd.get("hcard:hcard")
		if hd:
			hd = vcard.compose(hd)

			address = vcard.addresses(hd, join = True, best = "")
			if address:
				return	address

#
#
class AbstractAddressGeocoder:
	"""Geocode an API using an address (i.e. a single string)."""

	def IterGeocode(self, address):
		"""Iterate through all geocoding results for 'address'
		Results are lat/lon tuples.
		"""

		raise	NotImplementedError

	def Geocode(self, address):
		"""Return the first lat/lon geocoding result for address (or None)"""

		raise	NotImplementedError

class GoogleAddressGeocoder(AbstractAddressGeocoder):
	def __init__(self, *av, **ad):
		pass

	def IterGeocode(self, address):
		api = api_google.LocalSearch(q = address, _atom_like = False)
			
		for llitem in api.IterItems():
			lat = bm_extract.as_float(llitem, 'lat', otherwise = None)
			lng = bm_extract.as_float(llitem, 'lng', otherwise = None)

			if lat != None and lng != None :
				yield	( lat, lng )

	def Geocode(self, address):
		for ll in self.IterGeocode(address):
			return	ll

if __name__ == '__main__':
	bm_cfg.cfg.initialize()

	items = [
		{
			"address" : {
				"Street" : "310 Lawrence Avenue West",
				"City" : "North York",
			},
		},
	]

	def demonstrate_api(items):
		import api_google

		geocoder = GeocodeIterator()
		geocoder.request = {
			"street-address" : "{{ address.Street }}",
			"locality" : """{{ address.City|otherwise:"Toronto"}}""",
			"region" : """{{ address.Province|otherwise:"Ontario"}}""",
			"country-name" : """{{ address.Country|otherwise:"Canada" }}""",
		}
		geocoder.items = items
		for item in geocoder.items:
			pprint.pprint(item)

	def demonstrate_gsearch():
		api = GoogleAddressGeocoder()
		print api.Geocode("179 Old Orchard Grove, Toronto, Ontario")

	Log.verbose = False
	demonstrate_api(items)
	## demonstrate_gsearch()
